#include "StdAfx.hpp"
#include "AttractorFractals.hpp"
#include "Framework.hpp"
#include "D3dUtils.hpp"
#include "Multishader.hpp"


////////////////////////////////////////////////////////////////////////////////
// class AttractorFractalScreen

namespace {

struct VERTEX
{
	VEC3 Pos;
	COLOR Color;
};

} // namespace


AttractorFractalScreen::AttractorFractalScreen()
: m_VertexCount(0)
, m_LineCount(0)
{
	m_TwccFractalGenerate = TwCallbackContext(this, 1);
}

AttractorFractalScreen::~AttractorFractalScreen()
{

}

void AttractorFractalScreen::Init()
{
	m_VertexDecl.AddByUsage(0, D3DDECLUSAGE_POSITION, D3DDECLTYPE_FLOAT3, 0);
	m_VertexDecl.AddByUsage(0, D3DDECLUSAGE_COLOR,    D3DDECLTYPE_D3DCOLOR, 0);

	VEC3 currPos = VEC3(70.f, -9.f, 10.f), prevPos;
	uint iterCount = 30000;
	float dt = 0.01f;

	m_VertexCount = iterCount+1;
	m_LineCount = iterCount;

	IDirect3DVertexBuffer9 *vb;
	ERR_GUARD_DIRECTX( frame::Dev->CreateVertexBuffer(m_VertexCount*sizeof(VERTEX), D3DUSAGE_WRITEONLY, 0, D3DPOOL_MANAGED, &vb, NULL) )
	m_Vb.reset(vb);

	IDirect3DIndexBuffer9 *ib;
	ERR_GUARD_DIRECTX( frame::Dev->CreateIndexBuffer(m_LineCount*2*sizeof(uint2), D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &ib, NULL) )
	m_Ib.reset(ib);

	const float r = g_Rand.RandFloat(9.f, 29.f);
	const float s = 10.f;
	const float b = 8.f/3.f;
	const float outScale = 0.1f;
	const COLOR color = COLOR(0xff202020u);

	{
		VertexBufferLock vbLock(vb, 0);
		IndexBufferLock ibLock(ib, 0);
		VERTEX *vbData = (VERTEX*)vbLock.GetData();
		uint2 *ibData = (uint2*)ibLock.GetData();

		vbData->Pos = currPos;
		vbData->Color = color;
		vbData++;
		
		*ibData = 0; ibData++;
		*ibData = 1; ibData++;

		uint2 vIndexPrev = 0, vIndexCurr = 1;
		for (uint i = 0; i < iterCount; i++, vIndexPrev++, vIndexCurr++)
		{
			prevPos = currPos;
			currPos += VEC3(
				s * (prevPos.y - prevPos.x),
				r * prevPos.x - prevPos.y - prevPos.x*prevPos.z,
				prevPos.x*prevPos.y - b*prevPos.z) * dt;
			
			vbData->Pos = currPos * outScale;
			vbData->Color = color;
			vbData++;
			
			*ibData = vIndexPrev; ibData++;
			*ibData = vIndexCurr; ibData++;
		}
	}
}

void AttractorFractalScreen::Update()
{

}

void AttractorFractalScreen::Render3d()
{
	D3DPERF_EVENT(L"Attractor Fractal");

	frame::Dev->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0xff000040, 1.f, 0);

	if (m_VertexCount == 0 || m_LineCount == 0)
		return;
	if (m_Vb.get() == NULL || m_Ib.get() == NULL)
		return;

	Multishader *shader = GetUniversalShader();
	if (shader == NULL)
		return;

	uint macroValues[] = {
		1, // MATRIX
		0, // CONSTANT_COLOR
		1, // VERTEX_COLOR
		0, // TEXTURE
		0, // COLOR_MATRIX
		0, // COLOR_ADD
	};
	ID3DXEffect *eff = shader->GetEffect(macroValues, Multishader::FLAG_BEGIN_LOADING);
	if (eff == NULL)
		return;

	UINT fooPasses;
	eff->Begin(&fooPasses, 0);
	eff->BeginPass(0);

	frame::Dev->SetRenderState(D3DRS_ZENABLE, TRUE);
	frame::Dev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
	frame::Dev->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);

	frame::Dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	frame::Dev->SetRenderState(D3DRS_BLENDOP, D3DBLENDOP_ADD);
	frame::Dev->SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_ONE);
	frame::Dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

	ERR_GUARD_DIRECTX( eff->SetMatrix("g_Matrix", &m_Camera.m_Camera.GetMatrices().GetViewProj()) )

	assert(g_VertexDeclMngr);
	ERR_GUARD_DIRECTX( frame::Dev->SetVertexDeclaration(g_VertexDeclMngr->GetD3dDecl(m_VertexDecl)) )

	ERR_GUARD_DIRECTX( frame::Dev->SetStreamSource(0, m_Vb.get(), 0, sizeof(VERTEX)) )
	ERR_GUARD_DIRECTX( frame::Dev->SetIndices(m_Ib.get()) )
	ERR_GUARD_DIRECTX( frame::Dev->DrawIndexedPrimitive(D3DPT_LINELIST, 0, 0, m_VertexCount, 0, m_LineCount) )
	frame::RegisterDrawCall(m_LineCount);

	eff->EndPass();
	eff->End();
}

bool AttractorFractalScreen::OnMouseMove( const common::VEC2 &Pos )
{
	return m_Camera.OnMouseMove(Pos);
}

bool AttractorFractalScreen::OnMouseButton( const common::VEC2 &Pos, frame::MOUSE_BUTTON Button, frame::MOUSE_ACTION Action )
{
	return m_Camera.OnMouseButton(Pos, Button, Action);
}

bool AttractorFractalScreen::OnMouseWheel( const common::VEC2 &Pos, float Delta )
{
	return m_Camera.OnMouseWheel(Pos, Delta);
}

void AttractorFractalScreen::TwButton( uint tag )
{

}

/*void AttractorFractalScreen::DescClick()
{

}

void AttractorFractalScreen::ResetView()
{

}
*/
